/**
 * Copyright (c) 2015-2017, Henry Yang 杨勇 (gismail@foxmail.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */package com.lambkit.service;

import cn.hutool.core.util.StrUtil;
import com.dtflys.forest.Forest;
import com.dtflys.forest.annotation.ForestClient;
import com.jfinal.aop.Aop;
import com.jfinal.aop.AopManager;
import com.lambkit.JFLambkit;
import com.lambkit.core.rpc.RpcConfig;

import java.io.Serializable;

/**
 * 接口服务对象
 */
public class ServiceObject implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public static final int CLIENT = 0;
	public static final int SERVER = 1;
	
	private Class<?> interfaceClass;
	private Class<?> implementClass;
	private Class<?> mockClass;
	private String group;
	private String version;
	private int port;
	private int type;
	private String code;//编号

	public <T> ServiceObject(Class<T> interfaceClass) {
		this.interfaceClass = interfaceClass;
		RpcConfig rpcConfig = JFLambkit.config(RpcConfig.class);
		this.group = rpcConfig.getDefaultGroup();
		this.version = rpcConfig.getDefaultVersion();
		this.port = rpcConfig.getDefaultPort();
		this.type = SERVER;
	}

	public <T> ServiceObject(Class<T> interfaceClass, Class<? extends T> implementClass) {
		this(interfaceClass);
		this.implementClass = implementClass;
		this.type = implementClass==null ? CLIENT : SERVER;
		if(implementClass!=null) {
			AopManager.me().addMapping(interfaceClass, implementClass);
		}
	}

	public <T> ServiceObject(Class<T> interfaceClass, T instance) {
		this(interfaceClass);
		this.implementClass = instance.getClass();
		this.type = implementClass==null ? CLIENT : SERVER;
		if(implementClass!=null) {
			AopManager.me().addMapping(interfaceClass, implementClass.getName());
		}
		AopManager.me().addSingletonObject(instance);
	}
	
	public <T> ServiceObject(Class<T> interfaceClass, Class<? extends T> implementClass, Class<? extends T> mockClass, String group, String version, int port) {
		this(interfaceClass, implementClass);
		this.mockClass = mockClass;
		this.group = group;
		this.version = version;
		this.port = port;
	}

	public boolean hasClass(String className) {
		if(className.equals(interfaceClass.getName())) {
			return true;
		}
		if(type == SERVER && implementClass != null) {
			if(className.equals(implementClass.getName())) {
				return true;
			}
		}
		return false;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T instance() {
		T t = null;
		if(type == SERVER) {
			if(implementClass!=null) {
				t = (T) Aop.get(implementClass);
			}
			if(t==null && mockClass!=null) {
				t = (T) Aop.get(mockClass);
			}
			if(t==null) {
//				System.out.println("ServiceObject: " + interfaceClass.getName()
//						+ " abstract: " + ServiceManager.me().isAbstract(interfaceClass)
//						+ " interface: " + interfaceClass.isInterface());
				boolean isInterface = interfaceClass.isInterface();
				if(!ServiceManager.me().isAbstract(interfaceClass) && !isInterface) {
//					System.out.println("ServiceObject: create class " + interfaceClass.getName());
					t = (T) Aop.get(interfaceClass);
				}
				if(t==null && isInterface) {
					ForestClient forestClient = interfaceClass.getAnnotation(ForestClient.class);
					if(forestClient!=null) {
						t = (T) Forest.client(interfaceClass);
					}
				}
			}
		} else if(type == CLIENT) {
//			t = (T) RpcKit.obtain(interfaceClass);
//			if(t==null && mockClass!=null) {
//				t = (T) RpcKit.obtain(interfaceClass, Aop.get(mockClass));
//			}
			if(t==null) {
				if(interfaceClass.isInterface()) {
					ForestClient forestClient = interfaceClass.getAnnotation(ForestClient.class);
					if(forestClient!=null) {
						t = (T) Forest.client(interfaceClass);
					}
				}
			}
		}
		return t;
	}
	
	public <T> T singleton(LambkitAopFactory aopFactory) {
		T t = null;
		if(type == SERVER) {
			if(implementClass!=null) {
				t = (T) aopFactory.getSingleton(implementClass);
			}
			if(t==null && mockClass!=null) {
				t = (T) aopFactory.getSingleton(mockClass);
			}
			if(t==null) {
				boolean isInterface = interfaceClass.isInterface();
				if(!ServiceManager.me().isAbstract(interfaceClass) && !isInterface) {
					t = (T) aopFactory.getSingleton(interfaceClass);
				}
				if(t==null && isInterface) {
					ForestClient forestClient = interfaceClass.getAnnotation(ForestClient.class);
					if(forestClient!=null) {
						t = (T) Forest.client(interfaceClass);
					}
				}
			}
		} else if(type == CLIENT) {
//			t = (T) RpcKit.obtain(interfaceClass);
//			if(t==null && mockClass!=null) {
//				t = (T) RpcKit.obtain(interfaceClass, aopFactory.getSingleton(mockClass));
//			}
			if(t==null) {
				if(interfaceClass.isInterface()) {
					ForestClient forestClient = interfaceClass.getAnnotation(ForestClient.class);
					if(forestClient!=null) {
						t = (T) Forest.client(interfaceClass);
					}
				}
			}
		}
		return t;
	}
	
	public String getGroup() {
		return group;
	}
	public void setGroup(String group) {
		this.group = group;
	}
	public String getVersion() {
		return version;
	}
	public void setVersion(String version) {
		this.version = version;
	}
	public int getPort() {
		return port;
	}
	public void setPort(int port) {
		this.port = port;
	}
	public Class<?> getInterfaceClass() {
		return interfaceClass;
	}
	public void setInterfaceClass(Class<?> interfaceClass) {
		this.interfaceClass = interfaceClass;
	}
	public Class<?> getImplementClass() {
		return implementClass;
	}
	public void setImplementClass(Class<?> implementClass) {
		this.implementClass = implementClass;
	}

	public Class<?> getMockClass() {
		return mockClass;
	}

	public void setMockClass(Class<?> mockClass) {
		this.mockClass = mockClass;
	}
	
	public int getType() {
		return type;
	}
	
	public void setType(int type) {
		this.type = type;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	@Override
	public boolean equals(Object obj) {

		if(obj instanceof ServiceObject) {
			ServiceObject sobj = (ServiceObject) obj;
			if(!sobj.getInterfaceClass().getName().equals(this.getInterfaceClass().getName())) {
				return false;
			}
			if(sobj.getImplementClass()!=null) {
				if(!sobj.getImplementClass().getName().equals(this.getImplementClass().getName())) {
					return false;
				}
			} else {
				if(this.getImplementClass()!=null) {
					return false;
				}
			}
			
			if(sobj.getMockClass()!=null) {
				if(!sobj.getMockClass().getName().equals(this.getMockClass().getName())) {
					return false;
				}
			} else {
				if(this.getMockClass()!=null) {
					return false;
				}
			}
			
			if(StrUtil.isNotBlank(sobj.getGroup())) {
				if(!sobj.getGroup().equals(this.getGroup())) {
					return false;
				}
			} else {
				if(StrUtil.isNotBlank(this.getGroup())) {
					return false;
				}
			}
			
			if(StrUtil.isNotBlank(sobj.getVersion())) {
				if(!sobj.getVersion().equals(this.getVersion())) {
					return false;
				}
			} else {
				if(StrUtil.isNotBlank(this.getVersion())) {
					return false;
				}
			}
			
			if(sobj.getPort()!=this.getPort()) {
				return false;
			}
			
			if(sobj.getType()!=this.getType()) {
				return false;
			}
			
			if(StrUtil.isNotBlank(sobj.getCode())) {
				if(!sobj.getCode().equals(this.getCode())) {
					return false;
				}
			} else {
				if(StrUtil.isNotBlank(this.getCode())) {
					return false;
				}
			}
		}
		return false;
	}
}
